# Autor: Alisson Oliveira
from abc import ABCMeta, abstractmethod
import threading
from threading import Timer, Thread
from abc import *

class Runnable(object):
   """
      Abstract Class.
   """   
   __metaclass__ = ABCMeta
   
   @abstractmethod
   def run(self):
      """
         This Method must be overriden by subclasses.
      """
      pass

class ScheduledThread(Thread):
   
   def __init__(self, runnable, delay):
      """
          Executes the run method of runnable after dalay time.
      """
      self.runnable = runnable
      self.delay = delay
      self.scheduler = Timer
      Thread.__init__(self)
      
   def run(self):
      """
         Executes the run method of runnable.
      """
      self.scheduler(self.delay, self.runnable.run).start()

class ScheduledThreadAtFixedRate(Thread):
   
   def __init__(self, runnable, initial, delay):
      """ (Runnable, number, number)

         Executes the run method of runnable with intervals of delay after
         initial time.

         Returns an instance of ScheduledThreadAtFixedRate, which executes
         run method forever in an interval of delay in seconds. It can be stopped
         using ScheduledThreadAtFixedRate.cancel method.
         
      """
      self.active = None
      self._canceled = False
      self.delay = delay
      self.runnable = runnable
      self.scheduler = Timer
      self.scheduler(initial, self.run).start()
      Thread.__init__(self)

   def run(self):
      """
         Executes the run method of runnable class.
      """
      if not self._canceled:
         self.runnable.run()
         self.active = self.scheduler(self.delay, self.run)
         self.active.start()

   def cancel(self):
      """
         Stops the execution of Thread.
      """
      ThreadPoolManager.getInstance().cancelScheduled(self)
      self._canceled = True
      if self.active:
         self.active.cancel()
         self.active = None
      
class ThreadPoolManager(object):
   def __init__(self):
      """
         Class Manager to threading.
      """
      self.Scheduler = ScheduledThread
      self.SchedulerAtRate = ScheduledThreadAtFixedRate
      self.Executor = Timer
      self.active_schedules = []

   @staticmethod
   def getInstance():
      """
         Return the unique instance of this Manager.
      """
      return SingletonHolder.instance

   def generalExecuter(self, func, delay, *args, **kargs):
      """ (function, Number, tuple, dict) -> None.

         Executes the func which receives args and kargs after delay time.
      """
      self.Executor(delay, func, *args, **kargs).start()

   def scheduleGeneral(self, runnable, delay=0):
      """ (Runnable, Number) -> None.

         Executes the run method of runnable after dalay time.
      """
      self.Scheduler(runnable, delay).start()

   def scheduleAtFixedRate(self, runnable, initial, delay):
      """ (Runnable, Number, Number) -> ScheduledThreadAtFixedRate.

         Executes the run method of runnable with intervals of delay after
         initial time.

         Returns an instance of ScheduledThreadAtFixedRate, which executes
         run method forever in an interval of delay in seconds. It can be stopped
         using ScheduledThreadAtFixedRate.cancel method.
      """
      scheduled = self.SchedulerAtRate(runnable, initial, delay)
      self.active_schedules.append(scheduled)
      return scheduled

   def cancelScheduled(self, scheduled):
      if scheduled in self.active_schedules:
         self.active_schedules.remove(scheduled)

   def cancelAllSchedules(self):
      for schedule in self.active_schedules:
         schedule.cancel()
         

class SingletonHolder(object):
   """
      Holds unique instance to ThreadPoolManager.
   """
   instance = ThreadPoolManager()


if __name__ == '__main__':
   
   class Mytest(Runnable):
   
      def run(self):
         print "running runnable"
      
   c = Mytest()
   a = ThreadPoolManager.getInstance().scheduleAtFixedRate(c,5,5)
   b = ThreadPoolManager.getInstance().generalExecuter(a.cancel, 20)
   d = ThreadPoolManager.getInstance().scheduleGeneral(c)

